package com.dynamic.mybatis.web.security;

import com.alibaba.fastjson2.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;
import com.dynamic.mybatis.core.parser.NamespaceHelper;
import com.dynamic.mybatis.core.utils.StringUtils;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.http.HttpHeaders;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class SecuritySessionContextHolder {
    public static void main(String[] args) {
        String secret = "!FAFSDFD";
        String secret1 = "!FAFSDFD";
        Map<String, Object> payload = new HashMap<>();
        payload.put("userId", 123456); //payload
        payload.put("username", "zhangSan");
        String sign = JWT.create()
                .withPayload(payload)
                .sign(Algorithm.HMAC256(secret)); //签名 相当于秘钥
        System.out.println(sign);

        Verification verification = JWT.require(Algorithm.HMAC256(secret1));
        DecodedJWT decodedJWT = verification.build().verify(sign);
        Map<String, Object> param = new HashMap<>();
        decodedJWT.getClaims().forEach((k, v) -> {
            param.put(k, decodedJWT.getClaim(k));
        });
    }

    public static final String secret = "!FAFSDFD";
    public static final String session_context = "_session_context_";
    public static final String ts = "_ts_";

    //用户私有变量 key md5(jwt token)  value 回话参数
    static Cache<String, SecuritySessionContext> securityContextCache = Caffeine.newBuilder()
            .expireAfterAccess(30, TimeUnit.MINUTES)
            .removalListener((k, v, removalCause) -> {
                System.out.println("caffeine expired. " + k + "   " + v + "  " + removalCause);
            })
            .build();


    public static <T> T get() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String authorization = requestAttributes.getRequest().getHeader(HttpHeaders.AUTHORIZATION);
        return getContext(authorization);
    }

    public static <T> T get(String token) {
        return getContext(token);
    }

    public static <T> void set(String token, T context) {
        securityContextCache.put(getMd5Token(token), new SecuritySessionContext(token, context));
    }


    public static <T> String generateToken(T payload) {
        return JWT.create()
                .withClaim(session_context, JSON.toJSONString(payload))
                .sign(Algorithm.HMAC256(secret)); //签名 相当于秘钥
    }


    private static String getMd5Token(String token) {
        return DigestUtils.md5DigestAsHex(token.getBytes());
    }

    public static String getTenantCode() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String authorization = requestAttributes.getRequest().getHeader(HttpHeaders.AUTHORIZATION);
        SecuritySessionContext sessionContext = getSessionContext(authorization);
        return sessionContext != null ? sessionContext.getTenantCode() : NamespaceHelper.DEFAULT_TENANT;
    }


    private static <T> T getContext(String token) {
        SecuritySessionContext<T> sessionContext = getSessionContext(token);
        return sessionContext != null ? sessionContext.getContext() : null;
    }

    public static <T> SecuritySessionContext<T> getSessionContext(String token) {
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        //获取token对应的本地缓存
        String key = getMd5Token(token);
        return securityContextCache.getIfPresent(key);
    }

    private static DecodedJWT verifyToken(String token) {
        Verification verification = JWT.require(Algorithm.HMAC256(secret));
        return verification.build().verify(token);
    }


}
